<template>
  <div class="dag-editor">
    <div class="toolbar">
      <a-space>
        <a-button type="primary" size="small" @click="addDataSource">
          <template #icon>
            <IconCloud />
          </template>
          数据源
        </a-button>
        <a-button type="primary" size="small" @click="addProcessor">
          <template #icon>
            <IconSettings />
          </template>
          处理器
        </a-button>
        <a-button type="primary" size="small" @click="addOutput">
          <template #icon>
            <IconExport />
          </template>
          输出
        </a-button>
        <a-divider direction="vertical" />
        <a-button size="small" @click="autoLayout">
          <template #icon>
            <IconBranch />
          </template>
          自动布局
        </a-button>
        <a-button size="small" @click="startAnimation">
          <template #icon>
            <IconPlayArrow />
          </template>
          运行
        </a-button>
        <a-button size="small" @click="stopAnimation">
          <template #icon>
            <IconPause />
          </template>
          停止
        </a-button>
        <a-divider direction="vertical" />
        <a-button size="small" @click="clearGraph">
          <template #icon>
            <IconDelete />
          </template>
          清空
        </a-button>
        <a-button size="small" @click="zoomToFit">
          <template #icon>
            <IconFullscreen />
          </template>
          适应
        </a-button>
      </a-space>
    </div>
    <div ref="container" class="graph-container"></div>
  </div>
</template>

<script setup lang="ts">
  import { Edge, Graph, Path, StringExt } from '@antv/x6';
  import { Selection } from '@antv/x6-plugin-selection';
  import {
    IconBranch,
    IconCloud,
    IconDelete,
    IconExport,
    IconFullscreen,
    IconPause,
    IconPlayArrow,
    IconSettings,
  } from '@arco-design/web-vue/es/icon';
  import { onMounted, onUnmounted, ref } from 'vue';

  const container = ref<HTMLDivElement>();
  let graph: Graph;

  // 节点类型
  const NodeType = {
    INPUT: 'INPUT',
    FILTER: 'FILTER',
    JOIN: 'JOIN',
    UNION: 'UNION',
    AGG: 'AGG',
    OUTPUT: 'OUTPUT',
  } as const;

  type NodeTypeValue = (typeof NodeType)[keyof typeof NodeType];

  // 元素校验状态
  const CellStatus = {
    DEFAULT: 'default',
    SUCCESS: 'success',
    ERROR: 'error',
  } as const;

  type CellStatusValue = (typeof CellStatus)[keyof typeof CellStatus];

  // 不同节点类型的icon
  const NODE_TYPE_LOGO = {
    INPUT:
      'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*RXnuTpQ22xkAAAAAAAAAAAAADtOHAQ/original',
    FILTER:
      'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*ZJ6qToit8P4AAAAAAAAAAAAADtOHAQ/original',
    JOIN: 'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*EHqyQoDeBvIAAAAAAAAAAAAADtOHAQ/original',
    UNION:
      'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*k4eyRaXv8gsAAAAAAAAAAAAADtOHAQ/original',
    AGG: 'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*TKG8R6nfYiAAAAAAAAAAAAAADtOHAQ/original',
    OUTPUT:
      'https://mdn.alipayobjects.com/huamei_f4t1bn/afts/img/A*zUgORbGg1HIAAAAAAAAAAAAADtOHAQ/original',
  };

  // 根据节点的类型获取ports
  const getPortsByType = (type: NodeTypeValue, nodeId: string) => {
    let ports = [];
    switch (type) {
      case NodeType.INPUT:
        ports = [{ id: `${nodeId}-out`, group: 'out' }];
        break;
      case NodeType.OUTPUT:
        ports = [{ id: `${nodeId}-in`, group: 'in' }];
        break;
      default:
        ports = [
          { id: `${nodeId}-in`, group: 'in' },
          { id: `${nodeId}-out`, group: 'out' },
        ];
        break;
    }
    return ports;
  };

  // 创建节点样式
  const createDAGNodeShape = () => {
    // 注册自定义形状
    Graph.registerNode(
      'data-processing-dag-node',
      {
        inherit: 'rect',
        width: 212,
        height: 48,
        attrs: {
          body: {
            strokeWidth: 1,
            stroke: '#d0d7de',
            fill: '#ffffff',
            rx: 4,
            ry: 4,
          },
          label: {
            refX: 106,
            refY: 24,
            textAnchor: 'middle',
            textVerticalAnchor: 'middle',
            fontSize: 12,
            fill: '#24292f',
            fontFamily: 'PingFangSC',
          },
          icon: {
            refX: 12,
            refY: 12,
            width: 24,
            height: 24,
          },
          status: {
            refX: 182,
            refY: 24,
            r: 4,
            fill: '#d0d7de',
          },
          more: {
            refX: 200,
            refY: 24,
            fontSize: 14,
            fill: '#656d76',
            textAnchor: 'middle',
            textVerticalAnchor: 'middle',
            text: '⋮',
          },
        },
        markup: [
          {
            tagName: 'rect',
            selector: 'body',
          },
          {
            tagName: 'image',
            selector: 'icon',
          },
          {
            tagName: 'text',
            selector: 'label',
          },
          {
            tagName: 'circle',
            selector: 'status',
          },
          {
            tagName: 'text',
            selector: 'more',
          },
        ],
        ports: {
          groups: {
            in: {
              position: 'left',
              attrs: {
                circle: {
                  r: 4,
                  magnet: true,
                  stroke: '#31d0c6',
                  strokeWidth: 2,
                  fill: '#fff',
                  style: { visibility: 'hidden' },
                },
              },
            },
            out: {
              position: 'right',
              attrs: {
                circle: {
                  r: 4,
                  magnet: true,
                  stroke: '#31d0c6',
                  strokeWidth: 2,
                  fill: '#fff',
                  style: { visibility: 'hidden' },
                },
              },
            },
          },
        },
      },
      true
    );
  };

  // 注册连线样式
  const registerConnector = () => {
    Graph.registerConnector(
      'curveConnector',
      (sourcePoint, targetPoint) => {
        const hgap = Math.abs(targetPoint.x - sourcePoint.x);
        const path = new Path();
        path.appendSegment(
          Path.createSegment('M', sourcePoint.x - 4, sourcePoint.y)
        );
        path.appendSegment(
          Path.createSegment('L', sourcePoint.x + 12, sourcePoint.y)
        );

        // 水平三阶贝塞尔曲线
        path.appendSegment(
          Path.createSegment(
            'C',
            sourcePoint.x < targetPoint.x
              ? sourcePoint.x + hgap / 2
              : sourcePoint.x - hgap / 2,
            sourcePoint.y,
            sourcePoint.x < targetPoint.x
              ? targetPoint.x - hgap / 2
              : targetPoint.x + hgap / 2,
            targetPoint.y,
            targetPoint.x - 6,
            targetPoint.y
          )
        );
        path.appendSegment(
          Path.createSegment('L', targetPoint.x + 2, targetPoint.y)
        );
        return path.serialize();
      },
      true
    );

    Edge.config({
      markup: [
        {
          tagName: 'path',
          selector: 'wrap',
          attrs: {
            fill: 'none',
            cursor: 'pointer',
            stroke: 'transparent',
            strokeLinecap: 'round',
          },
        },
        {
          tagName: 'path',
          selector: 'line',
          attrs: {
            fill: 'none',
            pointerEvents: 'none',
          },
        },
      ],
      connector: { name: 'curveConnector' },
      attrs: {
        wrap: {
          connection: true,
          strokeWidth: 10,
          strokeLinejoin: 'round',
        },
        line: {
          connection: true,
          stroke: '#A2B1C3',
          strokeWidth: 1,
          targetMarker: {
            name: 'classic',
            size: 6,
          },
        },
      },
    });

    Graph.registerEdge('data-processing-curve', Edge, true);
  };

  const createSampleDAG = () => {
    if (!graph) return;

    const createNode = (
      type: NodeTypeValue,
      x: number,
      y: number,
      name: string,
      status: CellStatusValue = CellStatus.DEFAULT
    ) => {
      const nodeId = StringExt.uuid();
      const ports = getPortsByType(type, nodeId);

      const node = graph.addNode({
        id: nodeId,
        shape: 'data-processing-dag-node',
        x,
        y,
        ports,
        attrs: {
          icon: {
            'xlink:href': NODE_TYPE_LOGO[type],
          },
          label: {
            text: name,
          },
          status: {
            fill: (() => {
              if (status === CellStatus.SUCCESS) return '#52c41a';
              if (status === CellStatus.ERROR) return '#ff4d4f';
              return '#d0d7de';
            })(),
          },
          more: {
            text: '⋮',
          },
        },
        data: {
          type,
          name,
          status,
        },
      });

      return node;
    };

    // 创建示例节点
    const input1 = createNode(
      NodeType.INPUT,
      50,
      100,
      '数据输入_1',
      CellStatus.SUCCESS
    );
    const filter = createNode(
      NodeType.FILTER,
      320,
      100,
      '数据筛选_1',
      CellStatus.SUCCESS
    );
    const join = createNode(
      NodeType.JOIN,
      590,
      100,
      '数据连接_1',
      CellStatus.SUCCESS
    );
    const output = createNode(
      NodeType.OUTPUT,
      860,
      100,
      '数据输出_1',
      CellStatus.ERROR
    );
    const input2 = createNode(
      NodeType.INPUT,
      320,
      200,
      '数据输入_2',
      CellStatus.SUCCESS
    );

    // 创建连接线
    const createEdge = (source: any, target: any) => {
      graph.addEdge({
        id: StringExt.uuid(),
        shape: 'data-processing-curve',
        source: {
          cell: source.id,
          port: `${source.id}-out`,
        },
        target: {
          cell: target.id,
          port: `${target.id}-in`,
        },
        attrs: {
          line: {
            stroke: '#52c41a',
            strokeWidth: 2,
          },
        },
        zIndex: -1,
      });
    };

    createEdge(input1, filter);
    createEdge(filter, join);
    createEdge(input2, join);
    createEdge(join, output);
  };

  const initGraph = () => {
    if (!container.value) return;

    graph = new Graph({
      container: container.value,
      width: container.value.offsetWidth,
      height: 600,
      background: {
        color: '#ffffff',
      },
      grid: {
        visible: true,
        type: 'dot',
        args: {
          color: '#f0f0f0',
          thickness: 1,
        },
      },
      panning: {
        enabled: true,
        eventTypes: ['leftMouseDown', 'mouseWheel'],
      },
      mousewheel: {
        enabled: true,
        modifiers: 'ctrl',
        factor: 1.1,
        maxScale: 1.5,
        minScale: 0.5,
      },
      highlighting: {
        magnetAdsorbed: {
          name: 'stroke',
          args: {
            attrs: {
              fill: '#fff',
              stroke: '#31d0c6',
              strokeWidth: 4,
            },
          },
        },
      },
      connecting: {
        snap: true,
        allowBlank: false,
        allowLoop: false,
        highlight: true,
        connector: 'curveConnector',
        createEdge() {
          return graph.createEdge({
            shape: 'data-processing-curve',
            attrs: {
              line: {
                strokeDasharray: '5 5',
              },
            },
            zIndex: -1,
          });
        },
        validateConnection({ sourceMagnet, targetMagnet }) {
          // 只能从输出链接桩创建连接
          if (
            !sourceMagnet ||
            sourceMagnet.getAttribute('port-group') === 'in'
          ) {
            return false;
          }
          // 只能连接到输入链接桩
          if (
            !targetMagnet ||
            targetMagnet.getAttribute('port-group') !== 'in'
          ) {
            return false;
          }
          return true;
        },
      },
      selecting: {
        enabled: true,
        rubberband: true,
        showNodeSelectionBox: true,
      },
      snapline: true,
      keyboard: true,
      clipboard: true,
    });

    // 使用选择插件
    graph.use(
      new Selection({
        multiple: true,
        rubberEdge: true,
        rubberNode: true,
        modifiers: 'shift',
        rubberband: true,
      })
    );

    // 节点悬停效果
    graph.on('node:mouseenter', ({ node }) => {
      const ports = node.getPorts();
      ports.forEach((port) => {
        node.setPortProp(port.id, 'attrs/circle', {
          fill: '#fff',
          stroke: '#31d0c6',
          strokeWidth: 2,
          r: 4,
          style: { visibility: 'visible' },
        });
      });
    });

    graph.on('node:mouseleave', ({ node }) => {
      const ports = node.getPorts();
      ports.forEach((port) => {
        node.setPortProp(port.id, 'attrs/circle', {
          fill: '#fff',
          stroke: '#31d0c6',
          strokeWidth: 2,
          r: 4,
          style: { visibility: 'hidden' },
        });
      });
    });

    // 创建示例DAG
    createSampleDAG();
  };

  const addDataSource = () => {
    if (!graph) return;
    const nodeId = StringExt.uuid();
    const ports = getPortsByType(NodeType.INPUT, nodeId);

    graph.addNode({
      id: nodeId,
      shape: 'data-processing-dag-node',
      x: Math.random() * 400 + 50,
      y: Math.random() * 300 + 50,
      ports,
      attrs: {
        icon: {
          'xlink:href': NODE_TYPE_LOGO[NodeType.INPUT],
        },
        label: {
          text: '新数据源',
        },
        status: {
          fill: '#d0d7de',
        },
        more: {
          text: '⋮',
        },
      },
      data: {
        type: NodeType.INPUT,
        name: '新数据源',
        status: CellStatus.DEFAULT,
      },
    });
  };

  const addProcessor = () => {
    if (!graph) return;
    const nodeId = StringExt.uuid();
    const ports = getPortsByType(NodeType.FILTER, nodeId);

    graph.addNode({
      id: nodeId,
      shape: 'data-processing-dag-node',
      x: Math.random() * 400 + 50,
      y: Math.random() * 300 + 50,
      ports,
      attrs: {
        icon: {
          'xlink:href': NODE_TYPE_LOGO[NodeType.FILTER],
        },
        label: {
          text: '新处理器',
        },
        status: {
          fill: '#d0d7de',
        },
        more: {
          text: '⋮',
        },
      },
      data: {
        type: NodeType.FILTER,
        name: '新处理器',
        status: CellStatus.DEFAULT,
      },
    });
  };

  const addOutput = () => {
    if (!graph) return;
    const nodeId = StringExt.uuid();
    const ports = getPortsByType(NodeType.OUTPUT, nodeId);

    graph.addNode({
      id: nodeId,
      shape: 'data-processing-dag-node',
      x: Math.random() * 400 + 50,
      y: Math.random() * 300 + 50,
      ports,
      attrs: {
        icon: {
          'xlink:href': NODE_TYPE_LOGO[NodeType.OUTPUT],
        },
        label: {
          text: '新输出',
        },
        status: {
          fill: '#d0d7de',
        },
        more: {
          text: '⋮',
        },
      },
      data: {
        type: NodeType.OUTPUT,
        name: '新输出',
        status: CellStatus.DEFAULT,
      },
    });
  };

  const autoLayout = () => {
    if (!graph) return;
    const nodes = graph.getNodes();
    const x = 50;
    const y = 100;

    nodes.forEach((node, index) => {
      node.setPosition(x + index * 270, y + (index % 2) * 100);
    });

    graph.zoomToFit({ padding: 20 });
  };

  let animationIntervals: number[] = [];

  const stopAnimation = () => {
    // 清除所有动画定时器
    animationIntervals.forEach((interval) => clearInterval(interval));
    animationIntervals = [];
    if (!graph) return;
    graph.getEdges().forEach((edge) => {
      edge.attr({
        line: {
          stroke: '#52c41a',
          strokeWidth: 2,
          strokeDasharray: 0,
          strokeDashoffset: 0,
        },
      });
    });
  };

  const startAnimation = () => {
    if (!graph) return;
    // 清除之前的动画
    stopAnimation();

    graph.getEdges().forEach((edge) => {
      edge.attr({
        line: {
          stroke: '#3471F9',
          strokeWidth: 2,
          strokeDasharray: '10 5',
        },
      });
      // 创建连续的动画效果
      let offset = 0;
      const interval = setInterval(() => {
        if (edge.isEdge()) {
          offset -= 1;
          edge.attr('line/strokeDashoffset', offset);
        } else {
          clearInterval(interval);
        }
      }, 50);
      animationIntervals.push(interval);
    });
  };

  const clearGraph = () => {
    if (!graph) return;
    graph.clearCells();
  };

  const zoomToFit = () => {
    if (!graph) return;
    graph.zoomToFit({ padding: 20 });
  };

  onMounted(() => {
    createDAGNodeShape();
    registerConnector();
    initGraph();

    // 监听窗口大小变化
    const resizeObserver = new ResizeObserver(() => {
      if (container.value && graph) {
        graph.resize(container.value.offsetWidth, 600);
      }
    });

    if (container.value) {
      resizeObserver.observe(container.value);
    }
  });

  onUnmounted(() => {
    stopAnimation();
    if (graph) {
      graph.dispose();
    }
  });
</script>

<style scoped>
  .dag-editor {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    background: #ffffff;
  }

  .toolbar {
    padding: 16px 20px;
    border-bottom: 1px solid #e5e7eb;
    background: #ffffff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  }

  .graph-container {
    flex: 1;
    position: relative;
    overflow: hidden;
    min-height: 600px;
    background: #ffffff;
  }

  :deep(.x6-node-selected .x6-widget-selection-box) {
    border: 2px solid #3471f9;
    border-radius: 6px;
  }

  :deep(.x6-edge-selected .x6-edge-tool-selection) {
    stroke: #3471f9;
  }

  /* 流动动画 */
  @keyframes running-line {
    0% {
      stroke-dashoffset: 0;
    }
    100% {
      stroke-dashoffset: -30;
    }
  }
</style>
